# -*- encoding:utf-8 -*-
"""
@作者：jiajun_Tang
@文件名：gui_interface.py
@时间：2023/7/26  上午 10:31
@文档说明:
"""

import cv2
import config
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QPixmap, QImage, QPen, QColor, QBrush, QFont
from PyQt5.QtWidgets import QMessageBox, QGraphicsScene, QGraphicsPixmapItem, QGraphicsTextItem, QLabel, \
    QStatusBar, QGraphicsEllipseItem, QGraphicsItem, QGraphicsLineItem, QGraphicsPolygonItem, QGraphicsPathItem

from data_acquisition.get_image import dis_consumer
from QMyWidgets.QMyDialog import QMyDialog
from single_glass_front import Ui_MainWindow


class query_window(QtWidgets.QMainWindow):
    def __init__(self, display_queue=None, height=None, all_width=None):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        # self.setFixedSize(self.width(), self.height())

        self.display_queue = display_queue
        self.height = height
        self.all_width = all_width

        # 添加状态栏
        self.statusbar = QStatusBar()
        self.labviewcorrd = QLabel('鼠标当前坐标点:')
        self.labviewcorrd.setMinimumWidth(150)
        self.statusbar.addWidget(self.labviewcorrd)
        self.labscenecorrd = QLabel('scene坐标：')
        self.labscenecorrd.setMinimumWidth(150)
        self.statusbar.addWidget(self.labscenecorrd)
        self.labitemcorrd = QLabel('item坐标：')
        self.labitemcorrd.setMinimumWidth(150)
        self.statusbar.addWidget(self.labitemcorrd)
        self.setStatusBar(self.statusbar)

        # 多选框事件
        # self.ui.checkBox_11.clicked.connect(self.query_formula)
        # self.ui.checkBox_21.clicked.connect

        # 使用graphicsView_coax显示图片
        self.ui.graphicsView_coax.scene_img = QGraphicsScene()
        self.ui.graphicsView_coax.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        self.ui.graphicsView_coax.setStyleSheet("background-color: lightgray;")
        self.ui.graphicsView_coax.setScene(self.ui.graphicsView_coax.scene_img)
        # 绘制一个点，防止QGraphicsScene绘图出错
        self.ui.graphicsView_coax.scene_img.addRect(0, 0, 1, 1, QPen(QColor(Qt.lightGray)), QBrush(QColor(Qt.gray)))

        # 使用graphicsView_back显示图片
        self.ui.graphicsView_back.scene_img = QGraphicsScene()
        self.ui.graphicsView_back.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        self.ui.graphicsView_back.setStyleSheet("background-color: lightgray;")
        self.ui.graphicsView_back.setScene(self.ui.graphicsView_back.scene_img)
        # 绘制一个点，防止QGraphicsScene绘图出错
        self.ui.graphicsView_back.scene_img.addRect(0, 0, 1, 1, QPen(QColor(Qt.lightGray)), QBrush(QColor(Qt.gray)))

        # 使用graphicsView_tilt显示图片
        self.ui.graphicsView_tilt.scene_img = QGraphicsScene()
        self.ui.graphicsView_tilt.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        self.ui.graphicsView_tilt.setStyleSheet("background-color: lightgray;")
        self.ui.graphicsView_tilt.setScene(self.ui.graphicsView_tilt.scene_img)
        # 绘制一个点，防止QGraphicsScene绘图出错
        self.ui.graphicsView_tilt.scene_img.addRect(0, 0, 1, 1, QPen(QColor(Qt.lightGray)), QBrush(QColor(Qt.gray)))

        # 点击视窗内容后的事件
        self.ui.graphicsView_coax.sigMousePressPoint.connect(self.select_point_event_coax)
        self.ui.graphicsView_back.sigMousePressPoint.connect(self.select_point_event_back)
        self.ui.graphicsView_tilt.sigMousePressPoint.connect(self.select_point_event_tilt)

        # self.ui.graphicsView_coax.sigMouseReleasePoint.connect(self.slotMouseReleasePoint)

        # 检测数据列表
        self.points_list_coax = []
        self.points_list_back = []
        self.points_list_tilt = []
        self.glass_id = 0  # 检测的玻璃片ID
        self.a = 0  # 临时变量

        self.b = 0
        self.star_polygonItem = None  # 主窗口内的QGraphicsItem对象
        self.OK_or_NG = ''

        # 用于判断玻璃品级
        # dict1 = {[{var:0} for var in defect_dict]; 'Tom': 93, 'Jim': 80, 'Lily': 100}

        # 绑定开始检测按钮
        # self.ui.pushButton_start.clicked.connect(self.get_points_new)

        self.get_points_new()

        # 绘制工位信息文字提示
        self.draw_title()

    def draw_title(self):
        font = QFont()
        font.setPointSize(16)

        # 绘制文字显示
        text_coax = QGraphicsTextItem("第一工位")
        text_coax.setDefaultTextColor(QColor(255, 0, 0))
        text_coax.setFont(font)
        self.ui.graphicsView_coax.scene_img.addItem(text_coax)

        # 绘制文字显示
        text_back = QGraphicsTextItem("第二工位")
        text_back.setDefaultTextColor(QColor(255, 0, 0))
        text_back.setFont(font)
        self.ui.graphicsView_back.scene_img.addItem(text_back)

        # 绘制文字显示
        text_tilt = QGraphicsTextItem("第三工位")
        text_tilt.setDefaultTextColor(QColor(255, 0, 0))
        text_tilt.setFont(font)
        self.ui.graphicsView_tilt.scene_img.addItem(text_tilt)

    def get_points_new(self):
        # 新建子线程
        self.thread = getPointsThread(self.display_queue, self.height, self.all_width)
        # 连接信号和用于画点的函数
        self.thread.sendsignal.connect(self.draw_dot_plot)
        self.thread.start()

    def draw_dot_plot(self, points_new):
        """ 获取点位，并根据不同的类型绘制不同的图像"""

        # 判断是否是同一片玻璃上的
        glass_title = points_new[0]  # 返回结果的头信息
        glass_id = glass_title[0]  # 玻璃片ID
        glass_pname = glass_title[1]  # 工位编码

        # 输出当前检测的玻璃的编号

        self.ui.label_OKNG.setText('第 ' + str(glass_id) + ' 片玻璃：')

        if glass_id == self.glass_id:
            # 同一片玻璃,不做处理
            print('同一片玻璃')
        else:
            print('非同一片玻璃')

            # 不是同一片玻璃的情况下，将绘图清空
            self.ui.graphicsView_coax.scene().clear()
            self.ui.graphicsView_back.scene().clear()
            self.ui.graphicsView_tilt.scene().clear()

            # 绘制工位信息提示
            self.draw_title()

            self.points_list_coax = []
            self.points_list_back = []
            self.points_list_tilt = []
            self.glass_id = glass_id
            self.star_polygonItem = None
            self.OK_or_NG = 'OK'

        # 删除元组列表第一个数据----即玻璃片ID数据
        del points_new[0]

        # print("points_new is {}".format(self.points_new))
        # 根据返回的检测信息绘制QGraphicsScene
        for point in points_new:
            self.OK_or_NG = 'NG'
            window_x = point[0]
            window_y = point[1]

            defect_type = point[2]  # 缺陷类型，选择图形 , defect_img, accuracy,
            # defect_length = point[5]  # 缺陷长度
            # defect_wide = point[6]  # 缺陷宽度

            # # 根据缺陷长度选择颜色
            # if 0.01 <= max(defect_length, defect_wide) < 0.05:
            #     color = QColor(Qt.blue)
            # elif 0.05 <= max(defect_length, defect_wide) < 0.10:
            #     color = QColor(Qt.yellow)
            #     self.OK_or_NG = 'NG'
            # elif 0.10 <= max(defect_length, defect_wide) < 0.15:
            #     color = QColor(255, 0, 255)
            #     self.OK_or_NG = 'NG'
            # elif 0.15 <= max(defect_length, defect_wide) < 0.20:
            #     color = QColor(0, 255, 255)
            #     self.OK_or_NG = 'NG'
            # elif 0.20 <= max(defect_length, defect_wide) < 0.50:
            #     color = QColor(Qt.red)
            #     self.OK_or_NG = 'NG'
            # else:
            #     color = QColor(Qt.black)
            #     self.OK_or_NG = 'NG'

            if defect_type in config.defect_color:
                color = config.defect_color[defect_type]
            else:
                color = config.defect_color["other"]

            if 'coax' == glass_pname:
                # 同轴光源工位下，绘制一个圆形
                self.ui.graphicsView_coax.scene_img.addEllipse(window_x + 5, window_y + 5, 10, 10, QPen(color),
                                                               QBrush(color))
                self.points_list_coax.append(point)
            elif 'back' == glass_pname:
                # 背光源工位下，绘制一个矩形
                self.ui.graphicsView_back.scene_img.addRect(window_x, window_y + 5, 10, 10, QPen(color),
                                                            QBrush(color))
                self.points_list_back.append(point)
            elif 'tilt' == glass_pname:
                # 斜光源条件下，绘制一个三角形
                points = [(int(window_x), int(window_y - 5)),
                          (int(window_x - 4), int(window_y + 4)),
                          (int(window_x + 4), int(window_y + 4))]
                point_start = [QtCore.QPointF(*point) for point in points]
                polygon = QtGui.QPolygonF(point_start)
                self.ui.graphicsView_tilt.scene_img.addPolygon(polygon, QPen(color),
                                                               QBrush(color))
                self.points_list_tilt.append(point)

        # 输出最终检测结果：玻璃是否为良品
        self.ui.label_OKNG.setStyleSheet("background-color: rgb(0, 255, 0);\n"
                                         "color: rgb(255, 0, 0);")
        self.ui.label_OKNG.setText(self.OK_or_NG)

    def select_point_event_coax(self, pt):
        """ 鼠标点击瑕疵坐标图案，放大瑕疵细节图像"""
        ptscene = self.ui.graphicsView_coax.mapToScene(pt)  # 把view坐标转换为场景坐标

        x1_coax = ptscene.x()  # 鼠标点击的坐标点-X点
        y1_coax = ptscene.y()  # 鼠标点击的坐标点-Y点
        # print('当前鼠标点击位置：', self.x1, self.y1)  # 鼠标点击位置的坐标点
        # 根据鼠标点击的坐标点，判断触发事件
        # self.data_list = deepcopy(self.points_list)
        # 根据工位选择points
        for point in self.points_list_coax:
            # print(point[0], point[1])
            window_x = point[0]  # 缺陷点左上角X坐标
            window_y = point[1]  # 缺陷点左上角Y坐标

            # 鼠标点击位置匹配缺陷点位置
            if abs(x1_coax - window_x - 5) < 10 and abs(y1_coax - window_y - 5) < 10:

                if self.star_polygonItem:
                    self.ui.graphicsView_coax.scene_img.removeItem(self.star_polygonItem)
                    self.star_polygonItem = None

                self.insert_small_flags(pt)  # 插上小红旗
                self.interface_popup_defect(point)  # 弹出缺陷细节界面

    def select_point_event_back(self, pt):
        """ 鼠标点击瑕疵坐标图案，放大瑕疵细节图像"""
        ptscene_back = self.ui.graphicsView_back.mapToScene(pt)  # 把view坐标转换为场景坐标

        x1_back = ptscene_back.x()  # 鼠标点击的坐标点-X点
        y1_back = ptscene_back.y()  # 鼠标点击的坐标点-Y点
        # print('当前鼠标点击位置：', self.x1, self.y1)  # 鼠标点击位置的坐标点
        # 根据鼠标点击的坐标点，判断触发事件
        # self.data_list = deepcopy(self.points_list)
        for point in self.points_list_back:
            # print(point[0], point[1])
            window_x = point[0]  # 缺陷点左上角X坐标
            window_y = point[1]  # 缺陷点左上角Y坐标

            # 鼠标点击位置匹配缺陷点位置
            if abs(x1_back - window_x - 5) < 10 and abs(y1_back - window_y - 5) < 10:

                if self.star_polygonItem:
                    self.ui.graphicsView_back.scene_img.removeItem(self.star_polygonItem)
                    self.star_polygonItem = None

                self.insert_small_flags(pt)  # 插上小红旗
                self.interface_popup_defect(point)  # 弹出缺陷细节界面

    def select_point_event_tilt(self, pt):
        """ 鼠标点击瑕疵坐标图案，放大瑕疵细节图像"""
        ptscene = self.ui.graphicsView_coax.mapToScene(pt)  # 把view坐标转换为场景坐标

        x1_tilt = ptscene.x()  # 鼠标点击的坐标点-X点
        y1_tilt = ptscene.y()  # 鼠标点击的坐标点-Y点
        # print('当前鼠标点击位置：', self.x1, self.y1)  # 鼠标点击位置的坐标点
        # 根据鼠标点击的坐标点，判断触发事件
        # self.data_list = deepcopy(self.points_list)
        for point in self.points_list_tilt:
            # print(point[0], point[1])
            window_x = point[0]  # 缺陷点左上角X坐标
            window_y = point[1]  # 缺陷点左上角Y坐标

            # 鼠标点击位置匹配缺陷点位置
            if abs(x1_tilt - window_x - 5) < 10 and abs(y1_tilt - window_y - 5) < 10:

                if self.star_polygonItem:
                    self.ui.graphicsView_coax.scene_img.removeItem(self.star_polygonItem)
                    self.star_polygonItem = None

                self.insert_small_flags(pt)  # 插上小红旗
                self.interface_popup_defect(point)  # 弹出缺陷细节界面

    def interface_popup_defect(self, defect_details):
        """ 缺陷弹窗界面
        Args:
            defect_details: 缺陷详情
        """

        defect_category = defect_details[2]  # 缺陷类型
        # defect_type = defect_dict.get(defect_category)  # 缺陷类别
        defect_type = defect_category  # 缺陷类别，返回结果已包含中文内容
        defect_img = defect_details[3]  # 缺陷的细节图
        accuracy = defect_details[4]  # 缺陷类型置信度
        defect_length = defect_details[5]  # 缺陷长度
        defect_wide = defect_details[6]  # 缺陷宽度

        self.dialog_fault = QMyDialog()
        self.dialog_fault.resize(480, 360)
        self.dialog_fault.setWindowTitle('瑕疵细节')
        self.verticalLayout = QtWidgets.QVBoxLayout(self.dialog_fault)
        self.verticalLayout.setObjectName("verticalLayout")

        # height, width, channel = defect_img.shape
        # qtimg = QtGui.QImage(defect_img.data, width, height, QImage.Format_RGB888)

        qpixmapimg = self.cvimg_to_qpixmapimg(defect_img)

        self.pic = QPixmap(qpixmapimg)
        self.label_pic = QLabel("show", self.dialog_fault)
        self.label_pic.setPixmap(self.pic)
        self.verticalLayout.addWidget(self.label_pic)

        # 瑕疵细节图及属性
        self.verticalLayout1 = QtWidgets.QVBoxLayout()
        self.verticalLayout1.setObjectName("verticalLayout1")
        self.label_20 = QtWidgets.QLabel()
        self.label_20.setObjectName("label_20")
        self.label_20.setText("缺陷种类及概率：{},  {:.2f} %".format(defect_type, accuracy))
        self.verticalLayout1.addWidget(self.label_20)
        self.label_21 = QtWidgets.QLabel()
        self.label_21.setObjectName("label_21")
        self.label_21.setText(f"缺   陷   宽： \t {defect_wide}")
        self.verticalLayout1.addWidget(self.label_21)
        self.label_22 = QtWidgets.QLabel()
        self.label_22.setObjectName("label_22")
        self.label_22.setText(f"缺   陷   长： \t {defect_length}")
        self.verticalLayout1.addWidget(self.label_22)
        # self.label_23 = QtWidgets.QLabel()
        # self.label_23.setObjectName("label_23")
        # self.label_23.setText("缺 陷 面 积： \t XXX")
        # self.verticalLayout1.addWidget(self.label_23)

        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_24 = QtWidgets.QLabel()
        self.label_24.setObjectName("label_24")
        self.label_24.setText("检测是否正确：")
        self.horizontalLayout.addWidget(self.label_24)
        self.checkBox_21 = QtWidgets.QCheckBox()
        self.checkBox_21.setObjectName("checkBox_21")
        self.checkBox_21.setText("正确")
        self.horizontalLayout.addWidget(self.checkBox_21)
        self.checkBox_22 = QtWidgets.QCheckBox()
        self.checkBox_22.setObjectName("checkBox_21")
        self.checkBox_22.setText("错误")
        self.horizontalLayout.addWidget(self.checkBox_22)
        self.verticalLayout1.addLayout(self.horizontalLayout)

        self.verticalLayout.addLayout(self.verticalLayout1)

        self.dialog_fault.exec_()

    def insert_small_flags(self, pt):
        """ 鼠标点击窗口某一位置后，满足条件下，在鼠标点击位置插上小旗
        Args:
            pt:
        """

        self.labviewcorrd.setText('view坐标:{},{}'.format(pt.x(), pt.y()))
        ptscene = self.ui.graphicsView_coax.mapToScene(pt)  # 把view坐标转换为场景坐标

        self.x_real = ptscene.x()
        self.y_real = ptscene.y()

        self.labscenecorrd.setText('scene坐标:{:.0f},{:.0f}'.format(ptscene.x(), ptscene.y()))

        item = self.ui.graphicsView_coax.scene_img.itemAt(ptscene,
                                                          self.ui.graphicsView_coax.transform())  # 在场景某点寻找图元--最上面的图元
        # 返回值：图元地址  参数1 场景点坐标； 参数2 ？？？？
        if item is not None:
            ptitem = item.mapFromScene(ptscene)  # 把场景坐标转换为图元坐标
            self.labitemcorrd.setText('item坐标:{:.0f},{:.0f}'.format(ptitem.x(), ptitem.y()))
            # 这里可以设旗子
            # 绘制一个红旗
            points = [(int(ptscene.x()), int(ptscene.y())),
                      (int(ptscene.x()), int(ptscene.y() - 20)),
                      (int(ptscene.x() + 10), int(ptscene.y() - 15)),
                      (int(ptscene.x()), int(ptscene.y() - 10))]
            # self.polygon = QtGui.QPolygonF([QtCore.QPointF(*point) for point in points])

            polygon = QtGui.QPolygonF([QtCore.QPointF(*point) for point in points])
            # self.ui.graphicsView_coax.scene_img.addPolygon(self.polygon, QPen(QColor(Qt.red)),
            #                                           QBrush(QColor(Qt.red)))

            self.star_polygonItem = QGraphicsPolygonItem()
            self.star_polygonItem.setPolygon(polygon)
            self.star_polygonItem.setPen(QPen(Qt.red))
            self.star_polygonItem.setBrush(QBrush(Qt.red))
            # polygonItem.setPos(60, 260)
            self.ui.graphicsView_coax.scene_img.addItem(self.star_polygonItem)

    def mouseMoveEvent(self, EV):
        print('鼠标移动了')

    def mouse_move_event(self, pt):
        """ 鼠标移动触发事件"""
        self.labviewcorrd.setText('view坐标:{},{}'.format(pt.x(), pt.y()))
        ptscene = self.ui.graphicsView_coax.mapToScene(pt)  # 把view坐标转换为场景坐标

        self.x_real = ptscene.x()
        self.y_real = ptscene.y()
        # print(self.x_real, self.y_real)

    def cvimg_to_qpixmapimg(self, cvimg):
        """ 将opencv读取的ndarray图像，转化为 QtGui.QPixmap

        Args:
            cvimg: ndarray
        Return:
        """

        if len(np.shape(cvimg)) == 2:
            cvimg = cv2.cvtColor(cvimg, cv2.COLOR_GRAY2RGB)
        # else:
        #     cvimg = cvimg[:, :, ::-1]

        height, width, channel = cvimg.shape
        qtimg = QtGui.QImage(cvimg.data, width, height, QImage.Format_RGB888)
        qpixmapimg = QPixmap.fromImage(qtimg)
        return qpixmapimg


class getPointsThread(QThread):
    """
        声明用于传递数据到主线程的信号并指定传输的数据类型，
        此信号不能在__init__方法中声明，
        传递的数据类型支持标准的python数据类型，
        如字符串、日期、布尔类型，数字，列表，元组和字典。
    """
    sendsignal = pyqtSignal(list)

    def __init__(self, display_queue, height, width):
        super(getPointsThread, self).__init__()
        self.display_queue = display_queue
        self.SCALE_X = width / config.horizontal_graphics  # 水平方向放缩比例
        self.SCALE_Y = height / config.vertical_graphics  # 垂直方向放缩比例

    def __del__(self):
        self.wait()

    def run(self):
        while True:
            points_new = dis_consumer(self.display_queue, self.SCALE_X, self.SCALE_Y, rate=23,
                                      non_large_suppression=False)
            self.sendsignal.emit(points_new)


if __name__ == "__main__":
    import sys

    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)  # 添加对高分屏的支持
    app = QtWidgets.QApplication(sys.argv)
    window = query_window(display_queue=None, height=None, all_width=None)
    # window.showMaximized()  # 最大化窗口
    # window.setFixedSize(window.width(), window.height())
    window.show()
    sys.exit(app.exec_())
